package com.dingding.order.core.distance.biz.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.dingding.common.core.base.dto.ResponseEntity;
import com.dingding.common.core.beans.StagePriceCity;
import com.dingding.common.core.beans.Stageprice;
import com.dingding.common.core.beans.UserCoupon;
import com.dingding.common.core.config.SystemConfig;
import com.dingding.common.core.util.dto.CalculatDistanceDTO;
import com.dingding.facade.user.service.IUserInfoFacade;
import com.dingding.order.core.distance.biz.ICalculateDistanceService;
import com.dingding.order.core.distance.biz.abstracts.AbstractCalculatDistance;
import com.dingding.order.core.distance.dao.IStagePriceCityDao;
import com.dingding.order.core.distance.dao.IStagePriceDao;
import com.dingding.order.dto.distance.ReqCalculateDistanceDTO;
import com.dingding.order.dto.distance.ResCalculateDistance;
import com.dingding.order.dto.distance.ResCoupon;

/**
 * 
 * @ClassName: DistanceServiceImpl
 * @Description:订单距离相关业务
 * @author zhouxuan
 * @date 2016年6月3日 下午3:35:08
 * @version V2.2.0
 */
@Component("distanceService")
public class CalculateDistanceServiceImpl extends
		AbstractCalculatDistance<ResCalculateDistance, ReqCalculateDistanceDTO> implements ICalculateDistanceService {

	/**
	 * 所有城市的计价规则集合
	 */
	private static Map<Integer, List<Stageprice>> stagePriceMap = new HashMap<Integer, List<Stageprice>>();

	/**
	 * 阶段计价城市集合
	 */
	private static List<StagePriceCity> stagePriceCitys = null;

	@Resource(name = "stagePriceDao")
	private IStagePriceDao stagePriceDao; // 计价规则

	@Resource(name = "userInfoService")
	private IUserInfoFacade userInfoFacade; // 用户信息

	@Resource(name = "stagePriceCityDao")
	private IStagePriceCityDao stagePriceCityDao; // 计价规则城市

	/**
	 * @Description:计算距离价格
	 * @author fengshuonan
	 * @since 2.3.2
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public BigDecimal calcDistanceCost(ReqCalculateDistanceDTO reqCalculateDistanceDTO) {
		ResponseEntity<ResCalculateDistance> response = new ResponseEntity<ResCalculateDistance>();
		// 2:计算距离
		CalculatDistanceDTO calculatDistanceDTO = super.distance(reqCalculateDistanceDTO.getStartLongitude(),
				reqCalculateDistanceDTO.getStartLatitude(), reqCalculateDistanceDTO.getEndLongitude(),
				reqCalculateDistanceDTO.getEndLatitude());
		BigDecimal distance = calculatDistanceDTO.getDistance();

		// 3:根据距离计算订单费用
		BigDecimal orderCost = calculatePrice(distance, calculatDistanceDTO.getCityNo());
		// 5:获取平台费用
		BigDecimal platformCost = SystemConfig.getPlatformCost();
		// 6:获取优惠券
		ResCoupon resCoupon = getUserResCoupon(reqCalculateDistanceDTO);
		// 7:判断是否是长途
		int isLongDistance = isLongDistance(distance);
		// 拼装响应
		assemblyContent(response, distance, orderCost, platformCost, resCoupon, isLongDistance,
				reqCalculateDistanceDTO.getUserType());
		if(response.getResponseBody() == null){
			return null;
		}
		return response.getResponseBody().getOrderCost();
	}

	/**
	 * 
	 * @Title: calculateDistance
	 * @Description:计算距离和价格总方法
	 * @author CHEHAUANBO
	 * @since V2.2.0
	 * 
	 *        修改人：车焕波 修改原因：计算距离方法将变量变为对象 修改时间：2016-07-18 修改版本：V2.3.1
	 * 
	 * 
	 */
	@Override
	public String calculateDistance(String requestJson) {
		// 转换为私家车请求bean
		ReqCalculateDistanceDTO reqCalculateDistanceDTO = super.parseRequestJson(requestJson);
		ResponseEntity<ResCalculateDistance> response = new ResponseEntity<ResCalculateDistance>();

		// 1:验证请求参数
		if (!super.validateParam(response, reqCalculateDistanceDTO)) {
			return super.responseBeanToJson(response);
		}
		
		// 2:计算距离
		CalculatDistanceDTO calculatDistanceDTO = super.distance(reqCalculateDistanceDTO.getStartLongitude(),
				reqCalculateDistanceDTO.getStartLatitude(), reqCalculateDistanceDTO.getEndLongitude(),
				reqCalculateDistanceDTO.getEndLatitude());
		BigDecimal distance = calculatDistanceDTO.getDistance();

		// 3:根据距离计算订单费用
		BigDecimal orderCost = calculatePrice(distance, calculatDistanceDTO.getCityNo());
		// 5:获取平台费用
		BigDecimal platformCost = SystemConfig.getPlatformCost();
		// 6:获取优惠券
		ResCoupon resCoupon = getUserResCoupon(reqCalculateDistanceDTO);
		// 7:判断是否是长途
		int isLongDistance = isLongDistance(distance);
		// 拼装响应
		assemblyContent(response, distance, orderCost, platformCost, resCoupon, isLongDistance,
				reqCalculateDistanceDTO.getUserType());

		return super.responseBeanToJson(response);

	}

	/**
	 * 判断是否是长途
	 * 
	 * @Title: isLongDistance
	 * @param distance
	 *            订单里程
	 * @return int 1:短途 2：长途
	 * @author CHEHAUNBO
	 * @since V2.2.0
	 */
	private int isLongDistance(BigDecimal distance) {
		// 获取长途距离起点
		BigDecimal longDistance = SystemConfig.getLongDistance();
		if (distance.compareTo(longDistance) <= 0) {
			return 1; // 短途
		}
		return 2; // 长途
	}

	/**
	 * @Title: getUserResCoupon
	 * @Description:获取用户优惠券
	 * @author zhouxuan
	 * @return ResCoupon
	 */
	private ResCoupon getUserResCoupon(ReqCalculateDistanceDTO reqCalculateDistanceDTO) {
		ResCoupon resCoupon = new ResCoupon("", 0);
		// 缓存中没有信息的时候去数据库再获取一次
		if (reqCalculateDistanceDTO.getUserType() == 2) {// 用户类型1：车主 2：乘客
			List<UserCoupon> couponLists = this.userInfoFacade.getUserCouponList(reqCalculateDistanceDTO.getUserId());
			if (couponLists != null && couponLists.size() > 0) {
				UserCoupon temp = couponLists.get(0);
				for (UserCoupon userCoupon : couponLists) {
					if (temp.getCouponMoney().compareTo(userCoupon.getCouponMoney()) > 0) {
						temp = userCoupon;
					}
				}
				resCoupon.setUcId(temp.getUcId());
				resCoupon.setCouponMoney(temp.getCouponMoney());
			}
		}
		return resCoupon;
	}

	/**
	 * 
	 * 费用计算
	 * 
	 * @Title: calculatePrice
	 * @param distance
	 *            订单里程
	 * @param stageprices
	 *            计价规则表数据集合
	 * @return Double 计算后的总金额，保留一位小数
	 * @author CHEHUANBO
	 * @version V2.2.0
	 * 
	 *          修改人：车焕波 修改原因：需要根据城市编号获取不同城市的计价规则 修改时间：2016-07-14 修改版本：V2.3.1
	 * 
	 */
	private BigDecimal calculatePrice(BigDecimal distance, int cityNo) {

		// 总金额
		BigDecimal totalMoney = new BigDecimal(0);
		// 获取所有的计价规则
		List<Stageprice> stagePrices = this.getStageprices(cityNo);
		// 根据计价阶段进行升序排序
		Collections.sort(stagePrices);
		for (Stageprice stageprice : stagePrices) {

			// 阶段
			int stage = stageprice.getStage();
			// 单价（元/公里）
			BigDecimal price = stageprice.getPrice();
			// 当前阶段的起始范围
			BigDecimal startRange = stageprice.getStartRange();
			// 当前阶段的结束范围
			BigDecimal endRange = stageprice.getEndRange();

			if (stage == 1) { // 第一阶段,起步阶段无需计算只要在规定的公里数内，直接使用起步价。
				totalMoney = totalMoney.add(stageprice.getPrice());
				continue;
			}

			if (distance.compareTo(startRange) > 0) { // 符合当前阶段的计算范围（除第一阶段）

				if (stage == stagePrices.size()) { // 最后一阶段，由于最后阶段没有结束范围，所以，剩余没有计算的距离直接按最后阶段单价计算
					totalMoney = totalMoney.add((distance.subtract(startRange)).multiply(price));
					break;
				} else if (distance.compareTo(endRange) < 0) { // 如果小于当前阶段的最大值，直接使用下单距离减去当前阶段起始距离
					totalMoney = totalMoney.add((distance.subtract(startRange)).multiply(price));
					break;
				} else if (distance.compareTo(endRange) > 0) { // 如果大于当前阶段的最大值，使用当前阶段的距离差计算，剩余订单距离继续进行后续计算
					totalMoney = totalMoney.add((endRange.subtract(startRange)).multiply(price));
					continue;
				}
			}
		}

		return totalMoney.setScale(1,BigDecimal.ROUND_UP);

	}

	/**
	 * 
	 * 拼装响应给消费者的数据bean
	 * 
	 * @Title: assemblyContent
	 * @param distance
	 *            订单里程
	 * @param orderCost
	 *            订单费用（只包含里程费）
	 * @param platformCost
	 *            平台费用
	 * @param discount
	 *            优惠金额
	 * @param isLongDistance
	 *            是否长途
	 * @author CHEHAUNBO
	 * @since V2.2.0
	 * 
	 */
	private ResCalculateDistance assemblyContent(ResponseEntity<ResCalculateDistance> response, BigDecimal distance,
			BigDecimal orderCost, BigDecimal platformCost, ResCoupon resCoupon, int isLongDistance, int userType) {

		ResCalculateDistance resCalculateDistance = new ResCalculateDistance();
		resCalculateDistance.setDistance(distance); // 距离
		resCalculateDistance.setIsLongDistance(isLongDistance); // 1.短途2.长途
		resCalculateDistance.setOrderCost(orderCost); // 订单费用
		resCalculateDistance.setPlatformCost(platformCost); // 平台费用
		resCalculateDistance.setUcId(resCoupon.getUcId()); // 优惠券id
		resCalculateDistance.setCouponMoney(resCoupon.getCouponMoney()); // 优惠券金额
		if (userType == 1) {// 1：车主 2：乘客
			resCalculateDistance.setShareMoney(orderCost.multiply(new BigDecimal(0.8)).setScale(1, BigDecimal.ROUND_UP));
			resCalculateDistance.setTotalMoney(orderCost);
		} else {
			BigDecimal totalMoney = orderCost.add(platformCost).subtract(new BigDecimal(resCoupon.getCouponMoney()));
			if (totalMoney.compareTo(new BigDecimal(0)) < 0) {
				resCalculateDistance.setTotalMoney(new BigDecimal(0)); // 总价格（去掉折扣费用）
			} else {
				resCalculateDistance.setTotalMoney(totalMoney); // 总价格（去掉折扣费用）
			}
			resCalculateDistance.setShareMoney((orderCost.add(platformCost)).multiply(new BigDecimal(0.8)).setScale(1, BigDecimal.ROUND_UP));
		}
		response.setCode(HttpStatus.SC_OK);
		response.setResponseBody(resCalculateDistance);

		return resCalculateDistance;

	}

	/**
	 * 
	 * @Title: getStageprices
	 * @Description:获取全部计费规则
	 * @author zhouxuan
	 * @return List<Stageprice> 获取到的所有计费规则
	 * 
	 *         修改人：车焕波 修改时间：2016-07- 15 修改原因：由单城市计价改为多城市计价 修改版本：V2.3.0
	 * 
	 */
	protected synchronized List<Stageprice> getStageprices(int cityNo) {

		// 根据城市编号获取计价规则
		List<Stageprice> stagePrices = this.getStagePriceMap().get(cityNo);

		// 缓存中获取不到当前城市计费规则的时候，从数据库读取
		if (stagePrices == null) {
			// 验证计价城市是否存在，如果不存在使用其它
			boolean isExist = false;
			for (StagePriceCity stagePriceCity : getStagePriceCitys()) {
				int _cityNo = stagePriceCity.getCityNo();
				if (_cityNo == cityNo) {
					isExist = true;
				}
			}
			if (!isExist) {
				cityNo = -1; // 其它城市
			}
			stagePrices = this.stagePriceDao.getOrderStagePriceRule(cityNo);
			stagePriceMap.put(cityNo, stagePrices);
		}

		return stagePrices;

	}

	public Map<Integer, List<Stageprice>> getStagePriceMap() {
		return stagePriceMap;
	}

	public synchronized List<StagePriceCity> getStagePriceCitys() {

		if (stagePriceCitys == null) {
			stagePriceCitys = this.stagePriceCityDao.getStagePriceCity();
			if (stagePriceCitys == null) {
				stagePriceCitys = new ArrayList<StagePriceCity>();
			}
		}
		return stagePriceCitys;

	}

}
